package com.xtt._01_java8.StreamAPI;

import com.xtt.exceptionTest.SysException;
import org.junit.Test;

import java.util.*;
import java.util.concurrent.ForkJoinPool;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;

/**
 * @Author xtt.
 * @Description:
 * @Date:Create in 2017/9/12 0012 下午 21:29
 * @Modify By:
 */
public class TestSteamAPI {

    //创建Steam
    @Test
    public void createSteamAPI() {

        //数组
        String[] arr = {"1","2","3","4"};
        Stream<String> arr1 = Stream.of(arr);

        //集合
        List<String> list = Arrays.asList("1","2","3","4");
        Stream<String> stream = list.stream();

        //Stream.gengerate()
        Stream<Integer> generate = Stream.generate(() -> 1);

        //Stream.iterate
        //((1+1)+1)+1 ......
        Stream<Integer> iterate = Stream.iterate(1, (a) -> a + 1);

        //其他
        String str = "test123";
        IntStream chars = str.chars();
    }

    //
    @Test
    public void testStreamAPI1() {
        String[] arr = {"1","2","3","4"};
        Stream.of(arr).forEach(x -> System.out.println(x));
        Stream.of(arr).forEach(System.out::println);
    }

    @Test
    public void testStreamAPI2() {
        //limit 返回 Stream 的前面 n 个元素；
        // skip 则是扔掉前 n 个元素（它是由一个叫 subStream 的方法改名而来）。
        Integer[] arr = {6,9,5,7,8,4,3,2,1};
        Stream.of(arr).map(x -> {System.out.println("--:"+x);return x;}).limit(5).skip(1).forEach(System.out::println);//2,3
        System.out.println("------------");
        //有一种情况是 limit/skip 无法达到 short-circuiting 目的的，
        // 就是把它们放在 Stream 的排序操作后，
        // 原因跟 sorted 这个 intermediate 操作有关：此时系统并不知道 Stream 排序后的次序如何，
        // 所以 sorted 中的操作看上去就像完全没有被 limit 或者 skip 一样。
        Integer[] arr2 = {6,9,5,7,8,4,3,2,1};
        Arrays.stream(arr2).map(x -> {System.out.println("--:"+x);return x;}).sorted().limit(5).skip(1).forEach(System.out::println);

        //第一种map只输出9,5,7,8
        //第二种map中输出6,9,5,7,8,4,3,2,1，因为此时系统并不知道 Stream 排序后的次序如何
    }

    @Test
    public void testStreamAPI3() {
        String[] arr = {"1","2","3","4"};
        int res = Stream.of(arr).mapToInt(x -> Integer.parseInt(x)).max().getAsInt();
        System.out.println(res);

        //Integer res2 = Stream.of(arr).map(x -> Integer.parseInt(x)).max((a,b) -> a-b).get();
        Integer res2 = Stream.of(arr).map(x -> Integer.parseInt(x)).max(Comparator.comparingInt(x -> x)).get();
        System.out.println(res2);
    }

    @Test
    public void testSreamAPI4() {
        String[] arr = {"1","2","3","4","5","6","7","8"};
        Stream.of(arr)
                .mapToInt(x -> Integer.parseInt(x))
                .filter(x -> x%2 == 0)
                .sorted()
                .forEach(System.out::println);
    }

    @Test
    public void testStreamAPI5() {
        IntStream.range(1, 3).forEach(System.out::println);//1,2
        IntStream.rangeClosed(1, 3).forEach(System.out::println);//1,2,3
    }

    @Test
    public void testStreamAPI6() {
        String[] arr = {"a","b","3c","4d","5e","6f","7g","8h"};
        List<String> list = Arrays.stream(arr).map(String::toUpperCase).collect(Collectors.toList());
        System.out.println(list);//[A, B, 3C, 4D, 5E, 6F, 7G, 8H]

    }

    @Test
    public void testStreamAPI7() {
        //flatMap 把 input Stream 中的层级结构扁平化，就是将最底层元素抽出来放到一起，
        // 最终 output 的新 Stream 里面已经没有 List 了，都是直接的数字。
        Stream<List<Integer>> inputStream = Stream.of(Arrays.asList(1),
                Arrays.asList(2, 3),
                Arrays.asList(4, 5, 6)
        );
        Stream<Integer> outputStream = inputStream.
                flatMap((childList) -> childList.stream());
        outputStream.forEach(System.out::println);

    }

    @Test
    public void testStreamAPI8() {
        //peek 对每个元素执行操作并返回一个新的 Stream
        //peek 为终止操作，如果为foreach，stream 只能用一次，而peek会返回新的stream，所以可以多次执行
        //forEach 不能修改自己包含的本地变量值，也不能用 break/return 之类的关键字提前结束循环。
        List<String> collect = Stream.of("one", "two", "three", "four")
                .filter(e -> e.length() > 3)
                .peek(e -> System.out.println("Filtered value: " + e))
                .map(String::toUpperCase)
                .peek(e -> System.out.println("Mapped value: " + e))
                .collect(Collectors.toList());
        System.out.println(collect);

    }

    @Test
    public void testStreamAPI9() {
        //max
        //这是一个 termimal 兼 short-circuiting 操作，
        // 它总是返回 Stream 的第一个元素，或者空。
        //这里比较重点的是它的返回值类型：Optional。
        // 这也是一个模仿 Scala 语言中的概念，作为一个容器，它可能含有某值，或者不包含。
        // 使用它的目的是尽可能避免 NullPointerException。
        String[] arr = {"1","2","3","4","5","6","7","8"};
        OptionalInt max = Stream.of(arr).mapToInt(x -> Integer.parseInt(x)).max();
        System.out.println(max.isPresent());
        System.out.println(max.orElse(5));
        System.out.println(max.orElseThrow(() -> new NullPointerException()));

        String[] arr1 = {};
        OptionalInt max1 = Stream.of(arr1).mapToInt(x -> Integer.parseInt(x)).max();
        System.out.println(max1.isPresent());
        System.out.println(max1.orElse(5));
        System.out.println(max1.orElseThrow(() -> new NullPointerException()));
    }

    @Test
    public void testStreamAPI10() {
        //max
        //这是一个 termimal 兼 short-circuiting 操作，
        // 它总是返回 Stream 的第一个元素，或者空。
        //这里比较重点的是它的返回值类型：Optional。
        // 这也是一个模仿 Scala 语言中的概念，作为一个容器，它可能含有某值，或者不包含。
        // 使用它的目的是尽可能避免 NullPointerException。
        String[] arr = {"1","2","3","4","5","6","7","8"};
        //Optional<String> max = Stream.of(arr).max((a, b) -> a.length() - b.length());
        Optional<String> max = Stream.of(arr).findFirst();
        System.out.println(max.isPresent());
        System.out.println(max.orElse("5"));
        System.out.println(max.orElseThrow(() -> new NullPointerException()));

    }

    @Test
    public void testStreamAPI11() {
        //reduce
        // 这个方法的主要作用是把 Stream 元素组合起来。它提供一个起始值（种子），然后依照运算规则（BinaryOperator），和前面 Stream 的第一个、第二个、第 n 个元素组合。从这个意义上说，字符串拼接、数值的 sum、min、max、average 都是特殊的 reduce。例如 Stream 的 sum 就相当于
        //Integer sum = integers.reduce(0, (a, b) -> a+b); 或
        //Integer sum = integers.reduce(0, Integer::sum);
        //也有没有起始值的情况，这时会把 Stream 的前面两个元素组合起来，返回的是 Optional。
        // 字符串连接，concat = "ABCD"
        String concat = Stream.of("A", "B", "C", "D").reduce("", String::concat);
        System.out.println(concat);
        // 求最小值，minValue = -3.0
        double minValue = Stream.of(-1.5, 1.0, -3.0, -2.0).reduce(Double.MAX_VALUE, Double::min);
        System.out.println(minValue);
        // 求和，sumValue = 10, 有起始值
        int sumValue = Stream.of(1, 2, 3, 4).reduce(0, Integer::sum);
        System.out.println(sumValue);
        // 求和，sumValue = 10, 无起始值
        sumValue = Stream.of(1, 2, 3, 4).reduce(Integer::sum).get();
        System.out.println(sumValue);
        // 过滤，字符串连接，concat = "ace"
        concat = Stream.of("a", "B", "c", "D", "e", "F").
                filter(x -> x.compareTo("Z") > 0).
                reduce("", String::concat);
        System.out.println(concat);
        //上面代码例如第一个示例的 reduce()，第一个参数（空白字符）即为起始值，
        // 第二个参数（String::concat）为 BinaryOperator。这类有起始值的 reduce() 都返回具体的对象。
        // 而对于第四个示例没有起始值的 reduce()，由于可能没有足够的元素，返回的是 Optional，请留意这个区别
    }
    @Test
    public void testStreamAPI12() {
        //对 Stream 的排序通过 sorted 进行，
        // 它比数组的排序更强之处在于你可以首先对 Stream 进行各类 map、filter、limit、skip
        // 甚至 distinct 来减少元素数量后，再排序，这能帮助程序明显缩短执行时间。
        List<Person> persons = new ArrayList();
        for (int i = 1; i <= 5; i++) {
            Person person = new Person(i, "name" + i);
            persons.add(person);
        }
        List<Person> personList2 = persons.stream().limit(2).sorted((p1, p2) -> p1.getName().compareTo(p2.getName())).collect(Collectors.toList());
        System.out.println(personList2);

        Comparator<Person> comparator = Comparator.comparing(x -> x.getName());
        List<Person> personList3 = persons.stream().limit(2).sorted(comparator.reversed()).collect(Collectors.toList());
        System.out.println(personList3);
    }
    @Test
    public void testStreamAPI13() {
        //Stream 有三个 match 方法，从语义上说：
        /*allMatch：Stream 中全部元素符合传入的 predicate，返回 true
        anyMatch：Stream 中只要有一个元素符合传入的 predicate，返回 true
        noneMatch：Stream 中没有一个元素符合传入的 predicate，返回 true*/
        
    }

    @Test
    public void testStreamAPI14() {
        //设置并行计算
        String[] arr = {"a","b","3c","4d","5e","6f","7g","8h"};
        List<String> collect = Arrays.stream(arr)
                .parallel()//设置并行计算 采用的是ForkJoinPool线程池
                .peek(x -> System.out.println(Thread.currentThread().getName()))
                .map(String::toUpperCase)
                .collect(Collectors.toList());
        System.out.println(collect);
    }

    @Test
    public void testStreamAPI15() {
        //设置并行计算
        System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism","5");//设置并发数
        String[] arr = {"a","b","3c","4d","5e","6f","7g","8h"};
        List<String> collect = Arrays.stream(arr)
                .parallel()//设置并行计算 采用的是ForkJoinPool线程池可以通过设置参数控制并发数
                .peek(x -> System.out.println(Thread.currentThread().getName()))
                .map(String::toUpperCase)
                .collect(Collectors.toList());
        System.out.println(collect);
    }

    @Test
    public void testStreamAPI16() {
        String[] arr = {"a","b","3c","4d","5e","6f","7g","8h"};
        List<String> collect = Arrays.stream(arr)
                .sequential()//设置串行计算
                .peek(x -> System.out.println(Thread.currentThread().getName()))
                .map(String::toUpperCase)
                .collect(Collectors.toList());
        System.out.println(collect);
    }


}

class Person {
    public Person(Integer id, String name) {
        this.id = id;
        this.name = name;
    }

    private Integer id;
    private String name;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Person{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}
